package com.szcinda.service;

import com.szcinda.repository.*;
import com.szcinda.service.dto.organization.OrganizationService;
import com.szcinda.service.dto.role.RoleRepository;
import lombok.Data;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class CalculateService {
    private final ZadOrderRepository zadOrderRepository;
    private final GhOrderRepository ghOrderRepository;
    private final OrderFeeRepository orderFeeRepository;
    private final OrderFollowRepository orderFollowRepository;
    private final BusinessCodeRepository businessCodeRepository;
    private final CarrierRepository carrierRepository;
    private final ClientRepository clientRepository;
    private final RateRepository rateRepository;
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final OrganizationService organizationService;
    private final ContractRepository contractRepository;
    private final ContainerOrderRepository containerOrderRepository;

    public CalculateService(ZadOrderRepository zadOrderRepository, GhOrderRepository ghOrderRepository, OrderFeeRepository orderFeeRepository, OrderFollowRepository orderFollowRepository, BusinessCodeRepository businessCodeRepository, CarrierRepository carrierRepository, ClientRepository clientRepository, RateRepository rateRepository, UserRepository userRepository, RoleRepository roleRepository, OrganizationService organizationService, ContractRepository contractRepository, ContainerOrderRepository containerOrderRepository) {
        this.zadOrderRepository = zadOrderRepository;
        this.ghOrderRepository = ghOrderRepository;
        this.orderFeeRepository = orderFeeRepository;
        this.orderFollowRepository = orderFollowRepository;
        this.businessCodeRepository = businessCodeRepository;
        this.carrierRepository = carrierRepository;
        this.clientRepository = clientRepository;
        this.rateRepository = rateRepository;
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.organizationService = organizationService;
        this.contractRepository = contractRepository;
        this.containerOrderRepository = containerOrderRepository;
    }

    public DataDto todayOrderCountAndMoney(String userId, String orgId) {
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        DataDto dto = new DataDto();
        LocalDate now = LocalDate.now();
        Specification<ZadOrder> specification1 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), now.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), now.plusDays(1).atStartOfDay());
            predicates.add(timeEnd);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification1);
        Specification<GhOrder> specification2 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), now.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), now.plusDays(1).atStartOfDay());
            predicates.add(timeEnd);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<GhOrder> ghOrders = ghOrderRepository.findAll(specification2);
        dto.setCount(zadOrders.size() + ghOrders.size());
        List<String> orderIds = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        orderIds.addAll(ghOrders.stream().map(GhOrder::getId).collect(Collectors.toList()));
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(orderIds);
        List<Client> clients = clientRepository.findAll();
        List<Carrier> carriers = carrierRepository.findAll();
        clients.forEach(client -> {
            List<String> oIds = zadOrders.stream().filter(zadOrder -> zadOrder.getClientId().equals(client.getId())).map(ZadOrder::getId).collect(Collectors.toList());
            dto.addCount(TypeStringUtils.feeInType, client.getName(), oIds.size());
            List<OrderFee> clientFees = orderFees.stream().filter(fee -> oIds.contains(fee.getOrderId())).collect(Collectors.toList());
            clientFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeInType, client.getName());
                }
            });
            List<String> oIds2 = ghOrders.stream().filter(ghOrder -> ghOrder.getClientId().equals(client.getId())).map(GhOrder::getId).collect(Collectors.toList());
            dto.addCount(TypeStringUtils.feeInType, client.getName(), oIds2.size());
            clientFees = orderFees.stream().filter(fee -> oIds2.contains(fee.getOrderId())).collect(Collectors.toList());
            clientFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeInType, client.getName());
                }
            });
        });
        carriers.forEach(carrier -> {
            List<OrderFee> carrierFees = orderFees.stream().filter(fee -> StringUtils.hasText(fee.getCarrierId()) && fee.getCarrierId().equals(carrier.getId()) && TypeStringUtils.feeOutType.equals(fee.getType())).collect(Collectors.toList());
            Set<String> oIds = carrierFees.stream().map(OrderFee::getOrderId).collect(Collectors.toSet());
            dto.addCount(TypeStringUtils.feeOutType, carrier.getName(), oIds.size());
            carrierFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeOutType, carrier.getName());
                }
            });
        });
        return dto;
    }

    // 当月的单量和营收
    public DataDto thisMonthOrderCountAndMoney(String userId, String orgId) {
        // 获取当前机构及子机构
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        DataDto dto = new DataDto();
        // 构建查询日期
        LocalDate start = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
        LocalDate end = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).plusDays(1);
        // 构建查询条件
        Specification<ZadOrder> specification1 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), start.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), end.atStartOfDay());
            predicates.add(timeEnd);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification1);
        dto.setBlCount(zadOrders.size());
        Specification<GhOrder> specification2 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), start.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), end.atStartOfDay());
            predicates.add(timeEnd);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<GhOrder> ghOrders = ghOrderRepository.findAll(specification2);
        dto.setGhCount(ghOrders.size());
        dto.setCount(zadOrders.size() + ghOrders.size());
        List<String> orderIds = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        orderIds.addAll(ghOrders.stream().map(GhOrder::getId).collect(Collectors.toList()));
        // 查询出相关的费用列表
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(orderIds);
        List<Client> clients = clientRepository.findAll();
        List<Carrier> carriers = carrierRepository.findAll();
        clients.forEach(client -> {
            List<String> oIds = zadOrders.stream().filter(zadOrder -> zadOrder.getClientId().equals(client.getId())).map(ZadOrder::getId).collect(Collectors.toList());
            dto.addCount(TypeStringUtils.feeInType, client.getName(), oIds.size());
            List<OrderFee> clientFees = orderFees.stream().filter(fee -> oIds.contains(fee.getOrderId())).collect(Collectors.toList());
            clientFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeInType, client.getName());
                }
            });
            List<String> oIds2 = ghOrders.stream().filter(ghOrder -> ghOrder.getClientId().equals(client.getId())).map(GhOrder::getId).collect(Collectors.toList());
            dto.addCount(TypeStringUtils.feeInType, client.getName(), oIds2.size());
            clientFees = orderFees.stream().filter(fee -> oIds2.contains(fee.getOrderId())).collect(Collectors.toList());
            clientFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeInType, client.getName());
                }
            });
        });
        carriers.forEach(carrier -> {
            List<OrderFee> carrierFees = orderFees.stream().filter(fee -> StringUtils.hasText(fee.getCarrierId()) && fee.getCarrierId().equals(carrier.getId()) && TypeStringUtils.feeOutType.equals(fee.getType())).collect(Collectors.toList());
            Set<String> oIds = carrierFees.stream().map(OrderFee::getOrderId).collect(Collectors.toSet());
            dto.addCount(TypeStringUtils.feeOutType, carrier.getName(), oIds.size());
            carrierFees.forEach(orderFee -> {
                if (!StringUtils.isEmpty(orderFee.getCurrency())) {
                    dto.addFee(orderFee.getCurrency(), orderFee.getFeeCount(), TypeStringUtils.feeOutType, carrier.getName());
                }
            });
        });
        List<ExchangeRate> exchangeRates = rateRepository.findAll();
        double amount = 0;
        LocalDate now = LocalDate.now();
        for (OrderFee orderFee : orderFees) {
            // 过滤币种为空或者应付的费用
            if (StringUtils.isEmpty(orderFee.getCurrency()) || orderFee.getType().equals(TypeStringUtils.feeOutType))
                continue;

            // 转换币种
            if ("人民币".equals(orderFee.getCurrency()) || "RMB".equals(orderFee.getCurrency().toUpperCase())) {
                // 如果是人民币，直接相加
                amount += orderFee.getFeeCount();
                // 区分班列和国行费用
                zadOrders.stream().filter(zadOrder -> zadOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(zadOrder -> dto.addBLAmount(orderFee.getFeeCount()));
                ghOrders.stream().filter(ghOrder -> ghOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(ghOrder -> dto.addGHAmount(orderFee.getFeeCount()));
            } else {
                // 如果是其他币种，则需要换算
                for (ExchangeRate rate : exchangeRates) {
                    // 过期的汇率不算
                    if (rate.getStartDate() == null || rate.getEndDate() == null) {
                        continue;
                    }
                    if (rate.getStartDate().minusDays(1).isBefore(now) && now.isBefore(rate.getEndDate().plusDays(1))) {
                        // 正向汇率关系
                        if (rate.getFromRate().equals(orderFee.getCurrency()) && (rate.getToRate().toUpperCase().equals("人民币") || rate.getToRate().toUpperCase().equals("RMB"))) {
                            amount += orderFee.getFeeCount() * rate.getRate();
                            // 区分班列和国行费用
                            zadOrders.stream().filter(zadOrder -> zadOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(zadOrder -> dto.addBLAmount(orderFee.getFeeCount() * rate.getRate()));
                            ghOrders.stream().filter(ghOrder -> ghOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(ghOrder -> dto.addGHAmount(orderFee.getFeeCount() * rate.getRate()));
                        } else if (rate.getToRate().equals(orderFee.getCurrency()) && (rate.getFromRate().toUpperCase().equals("人民币") || rate.getFromRate().toUpperCase().equals("RMB"))) {
                            amount += orderFee.getFeeCount() / rate.getRate();
                            // 区分班列和国行费用
                            zadOrders.stream().filter(zadOrder -> zadOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(zadOrder -> dto.addBLAmount(orderFee.getFeeCount() / rate.getRate()));
                            ghOrders.stream().filter(ghOrder -> ghOrder.getId().equals(orderFee.getOrderId())).findFirst().ifPresent(ghOrder -> dto.addGHAmount(orderFee.getFeeCount() / rate.getRate()));
                        }
                    }

                }
            }
        }
        dto.setAmount(amount);
        return dto;
    }

    // 上月的每日单量和营收
    public List<DataDto> lastMonthOrderCountAndAmount(String userId, String orgId) {
        // 获取当前机构及子机构
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        boolean isAdmin = RoleType.SUPER_ADMIN.equals(role.getName()) || RoleType.ADMIN.equals(role.getName()) || role.isEqualAdmin();
        // 先查询一个月内的订单量
        LocalDate start = LocalDate.now().minusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
        LocalDate end = LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()).plusDays(1);
        Specification<ZadOrder> specification1 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), start.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), end.atStartOfDay());
            predicates.add(timeEnd);
            // 如果不是机构管理员，则只能看到自己的
            if (!isAdmin) {
                Predicate userIdEq = criteriaBuilder.equal(root.get("userId"), userId);
                predicates.add(userIdEq);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification1);
        Specification<GhOrder> specification2 = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("organizationId");
            predicates.add(exp.in(childOrgIds));
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), start.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), end.atStartOfDay());
            predicates.add(timeEnd);
            // 如果不是机构管理员，则只能看到自己的
            if (!isAdmin) {
                Predicate userIdEq = criteriaBuilder.equal(root.get("userId"), userId);
                predicates.add(userIdEq);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<String> ids = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        List<GhOrder> ghOrders = ghOrderRepository.findAll(specification2);
        ids.addAll(ghOrders.stream().map(GhOrder::getId).collect(Collectors.toList()));
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(ids);
        List<ExchangeRate> exchangeRates = rateRepository.findAll();
        List<DataDto> dataDto = new ArrayList<>();
        LocalDate now = LocalDate.now();
        // 得出当前月有多少天
        int lengthOfMonth = LocalDate.now().minusMonths(1).lengthOfMonth();
        LocalDate startDate = start;
        // 过滤每一天的单量进行汇总
        for (int i = 1; i < lengthOfMonth; i++) {
            DataDto dto = new DataDto();
            dto.setName(i + "号");
            LocalDate nextDate = startDate.plusDays(1);
            LocalDate finalStartDate = startDate;
            List<ZadOrder> zadOrderList = zadOrders.stream().filter(order -> order.getCreateTime().isAfter(finalStartDate.atStartOfDay()) && order.getCreateTime().isBefore(nextDate.atStartOfDay())).collect(Collectors.toList());
            List<GhOrder> ghOrderList = ghOrders.stream().filter(order -> order.getCreateTime().isAfter(finalStartDate.atStartOfDay()) && order.getCreateTime().isBefore(nextDate.atStartOfDay())).collect(Collectors.toList());
            startDate = startDate.plusDays(1);
            // 计算单量
            dto.setCount(zadOrderList.size() + ghOrderList.size());
            // 计算金额总数
            List<String> filterIds = zadOrderList.stream().map(ZadOrder::getId).collect(Collectors.toList());
            filterIds.addAll(ghOrderList.stream().map(GhOrder::getId).collect(Collectors.toList()));
            // 过滤出应收费用
            List<OrderFee> orderFilterFees = orderFees.stream().filter(orderFee -> TypeStringUtils.feeInType.equals(orderFee.getType()) && filterIds.contains(orderFee.getOrderId())).collect(Collectors.toList());
            double amount = 0;
            for (OrderFee orderFee : orderFilterFees) {

                if (StringUtils.isEmpty(orderFee.getCurrency())) continue;
                if ("人民币".equals(orderFee.getCurrency()) || "RMB".equals(orderFee.getCurrency().toUpperCase())) {
                    // 如果是人民币，直接相加
                    amount += orderFee.getFeeCount();
                } else {
                    // 如果是其他币种，则需要换算
                    for (ExchangeRate rate : exchangeRates) {
                        // 过期的汇率不算
                        if (rate.getStartDate() == null || rate.getEndDate() == null) {
                            continue;
                        }
                        if (rate.getStartDate().minusDays(1).isBefore(now) && now.isBefore(rate.getEndDate().plusDays(1))) {
                            // 正向汇率关系
                            if (rate.getFromRate().equals(orderFee.getCurrency()) && (rate.getToRate().toUpperCase().equals("人民币") || rate.getToRate().toUpperCase().equals("RMB"))) {
                                amount += orderFee.getFeeCount() * rate.getRate();
                            } else if (rate.getToRate().equals(orderFee.getCurrency()) && (rate.getFromRate().toUpperCase().equals("人民币") || rate.getFromRate().toUpperCase().equals("RMB"))) {
                                amount += orderFee.getFeeCount() / rate.getRate();
                            }
                        }
                    }
                }
            }
            dto.setAmount(BigDecimal.valueOf(amount).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue());
            dataDto.add(dto);
        }
        return dataDto;
    }

    // 未开票 未收款 未付款
    public List<String> getOrderExpiredList(String userId, String orgId) {
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        boolean isPersonal = false;
        if (role.isEqualAdmin()) {
            // 授予了机构权限
            isPersonal = true;
        }
        List<String> messages = new ArrayList<>();
        Map<String, String> map = new HashMap<>();
        // 未开票
        List<ZadOrder> zadOrders = zadOrderRepository.findInvoice(BillType.STATUS_WKP, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            zadOrders = zadOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (zadOrders.size() > 0) {
            for (ZadOrder zadOrder : zadOrders) {
                if (StringUtils.isEmpty(zadOrder.getOrderNumber())) continue;
                String text = "班列订单编号：" + zadOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应收未开票")) {
                        map.put(text, map.get(text) + "/应收未开票");
                    }
                } else {
                    map.put(text, "应收未开票");
                }
            }
        }
        zadOrders = zadOrderRepository.findOutInvoice(BillType.STATUS_WKP, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            zadOrders = zadOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (zadOrders.size() > 0) {
            for (ZadOrder zadOrder : zadOrders) {
                if (StringUtils.isEmpty(zadOrder.getOrderNumber())) continue;
                String text = "班列订单编号：" + zadOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应付未开票")) {
                        map.put(text, map.get(text) + "/应付未开票");
                    }
                } else {
                    map.put(text, "应付未开票");
                }
            }
        }
        zadOrders = zadOrderRepository.findSettlement(BillType.STATUS_WJS, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            zadOrders = zadOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (zadOrders.size() > 0) {
            for (ZadOrder zadOrder : zadOrders) {
                if (StringUtils.isEmpty(zadOrder.getOrderNumber())) continue;
                String text = "班列订单编号：" + zadOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应收未结算")) {
                        map.put(text, map.get(text) + "/应收未结算");
                    }
                } else {
                    map.put(text, "应收未结算");
                }
            }
        }
        zadOrders = zadOrderRepository.findOutSettlement(BillType.STATUS_WJS, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            zadOrders = zadOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (zadOrders.size() > 0) {
            for (ZadOrder zadOrder : zadOrders) {
                if (StringUtils.isEmpty(zadOrder.getOrderNumber())) continue;
                String text = "班列订单编号：" + zadOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应付未结算")) {
                        map.put(text, map.get(text) + "/应付未结算");
                    }
                } else {
                    map.put(text, "应付未结算");
                }
            }
        }
        List<GhOrder> ghOrders = ghOrderRepository.findInvoice(BillType.STATUS_WKP, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            ghOrders = ghOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (ghOrders.size() > 0) {
            ghOrders.forEach(ghOrder -> {
                String text = "综合物流订单号：" + ghOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应收未开票")) {
                        map.put(text, map.get(text) + "/应收未开票");
                    }
                } else {
                    map.put(text, "应收未开票");
                }
            });
        }
        ghOrders = ghOrderRepository.findOutInvoice(BillType.STATUS_WKP, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            ghOrders = ghOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (ghOrders.size() > 0) {
            ghOrders.forEach(ghOrder -> {
                String text = "综合物流订单号：" + ghOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应付未开票")) {
                        map.put(text, map.get(text) + "/应付未开票");
                    }
                } else {
                    map.put(text, "应付未开票");
                }
            });
        }
        ghOrders = ghOrderRepository.findSettlement(BillType.STATUS_WJS, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            ghOrders = ghOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (ghOrders.size() > 0) {
            ghOrders.forEach(ghOrder -> {
                String text = "综合物流订单号：" + ghOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应收未结算")) {
                        map.put(text, map.get(text) + "/应收未结算");
                    }
                } else {
                    map.put(text, "应收未结算");
                }
            });
        }
        ghOrders = ghOrderRepository.findOutSettlement(BillType.STATUS_WJS, childOrgIds);
        if (isPersonal) {
            // 如果是个人的，只需要查看自己的
            ghOrders = ghOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        if (ghOrders.size() > 0) {
            ghOrders.forEach(ghOrder -> {
                String text = "综合物流订单号：" + ghOrder.getOrderNumber();
                if (map.containsKey(text)) {
                    if (!map.get(text).contains("/应付未结算")) {
                        map.put(text, map.get(text) + "/应付未结算");
                    }
                } else {
                    map.put(text, "应付未结算");
                }
            });
        }
        map.forEach((key, value) -> {
            messages.add(key + " " + value);
        });
        return messages;
    }

    // 集装箱到期提醒
    public List<String> getContainerOrderExpiredList(String userId, String orgId) {
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        boolean isAdmin = RoleType.SUPER_ADMIN.equals(role.getName()) || RoleType.ADMIN.equals(role.getName()) || role.isEqualAdmin();
        List<String> messages = new ArrayList<>();
        LocalDate now = LocalDate.now();
        List<ContainerOrder> containerOrders = containerOrderRepository.findAllByPackageTypeOrPackageTypeIsNull(TypeStringUtils.packageType1);
        // 如果不是管理员，则只能看到自己的
        if (!isAdmin) {
            containerOrders = containerOrders.stream().filter(containerOrder -> userId.equals(containerOrder.getUserId())).collect(Collectors.toList());
        }
        containerOrders.forEach(containerOrder -> {
            if (childOrgIds.contains(containerOrder.getOrganizationId())) {
                if (TypeStringUtils.packageType1.equals(containerOrder.getPackageType()) && containerOrder.getPreReturnDate() != null) {
                    if (now.isAfter(containerOrder.getPreReturnDate())) {
                        messages.add("集装箱号：" + containerOrder.getCabinetNumber() + "已逾期未还,请及时处理");
                    } else {
                        long days = now.until(containerOrder.getPreReturnDate(), ChronoUnit.DAYS);
                        if (days <= 3) {
                            messages.add("集装箱号【" + containerOrder.getCabinetNumber() + "】将于【" + (days == 0 ? "今" : days) + "】天后到期,请及时处理");
                        }
                    }
                }
            }
        });
        return messages;
    }

    // 查找待办事项的数据
    public List<String> daiBanShiXiangData(String userId, String orgId) {
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        boolean isAdmin = RoleType.SUPER_ADMIN.equals(role.getName()) || RoleType.ADMIN.equals(role.getName()) || role.isEqualAdmin();
        List<String> messages = new ArrayList<>();
        Map<String, String> map = new HashMap<>();
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrganizationIdIn(childOrgIds);
        if (!isAdmin) {
            // 如果是个人的，只需要查看自己的
            zadOrders = zadOrders.stream().filter(zadOrder -> userId.equals(zadOrder.getUserId())).collect(Collectors.toList());
        }
        List<OrderFollow> orderFollows = orderFollowRepository.findByOrderIdIn(zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList()));
        for (ZadOrder zadOrder : zadOrders) {
            OrderFollow orderFollow = orderFollows.stream().filter(f -> f.getOrderId().equals(zadOrder.getId())).findFirst().orElse(new OrderFollow());
            String text = zadOrder.getOrderNumber();
            if (StringUtils.isEmpty(text)) {
                text = zadOrder.getCabinetNumber();
            }
            if (StringUtils.isEmpty(orderFollow.getCustomsFile())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供报关文件");
                } else {
                    map.put(text, "未提供报关文件");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getPreOrderConfirm())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未确认预录单");
                } else {
                    map.put(text, "未确认预录单");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getCarFile())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供随车文件");
                } else {
                    map.put(text, "未提供随车文件");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getPackageSolution())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供装箱方案");
                } else {
                    map.put(text, "未提供装箱方案");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getStFile())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供ST文件");
                } else {
                    map.put(text, "未提供ST文件");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getTransportOrder())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供运单");
                } else {
                    map.put(text, "未提供运单");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getCustomsReleaseOrder())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供报关放行单");
                } else {
                    map.put(text, "未提供报关放行单");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getArriveTime())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未到站");
                } else {
                    map.put(text, "未到站");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getDskAtbMrn())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供提重文件");
                } else {
                    map.put(text, "未提供提重文件");
                }
            }
            if (StringUtils.isEmpty(orderFollow.getPackagePic())) {
                if (map.containsKey(text)) {
                    map.put(text, map.get(text) + "/未提供装箱图片");
                } else {
                    map.put(text, "未提供装箱图片");
                }
            }
        }
        map.forEach((key, value) -> {
            messages.add(key + " " + value);
        });
        return messages;
    }

    // 订单未完成提醒
    public List<String> getOrderNoCompletedList(String userId, String orgId) {
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        List<String> messages = new ArrayList<>();
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrganizationIdIn(childOrgIds);
        List<OrderFollow> orderFollows = orderFollowRepository.findByOrderIdIn(zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList()));
        // 没有柜号的情况
        for (ZadOrder zadOrder : zadOrders) {
            if (StringUtils.isEmpty(zadOrder.getCabinetNumber())) {
                continue;
            }
            if (orderFollows.stream().noneMatch(orderFollow -> orderFollow.getOrderId().equals(zadOrder.getId()))) {
                messages.add("班列订单-柜号为[" + zadOrder.getCabinetNumber() + "]未完成,请及时处理");
            }
        }
        for (OrderFollow orderFollow : orderFollows) {
            if (!checkIsCompleted(orderFollow)) {
                zadOrders.stream().filter(zadOrder -> zadOrder.getId().equals(orderFollow.getOrderId())).findFirst().ifPresent(zadOrder -> messages.add("班列订单-柜号为[" + zadOrder.getCabinetNumber() + "]未完成,请及时处理"));
            }
        }
        return messages;
    }

    // 检查是否完成
    public boolean checkIsCompleted(OrderFollow orderFollow) {
        String completedText = "已审核完成";
//        return completedText.equals(orderFollow.getPackagePic()) && completedText.equals(orderFollow.getCustomsFile()) && completedText.equals(orderFollow.getCarFile()) && completedText.equals(orderFollow.getPackageSolution()) && completedText.equals(orderFollow.getStFile()) && completedText.equals(orderFollow.getPreOrderConfirm()) && completedText.equals(orderFollow.getTransportOrder()) && completedText.equals(orderFollow.getCustomsReleaseOrder());
        return completedText.equals(orderFollow.getPreOrderConfirm());
    }

    // 合同到期提醒
    public List<String> getContractExpireList(String userId, String orgId) {
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        boolean isAdmin = RoleType.SUPER_ADMIN.equals(role.getName()) || RoleType.ADMIN.equals(role.getName()) || role.isEqualAdmin();
        List<String> messages = new ArrayList<>();
        List<Client> clients = clientRepository.findByOrganizationIdIn(childOrgIds);
        List<Carrier> carriers = carrierRepository.findByOrganizationIdIn(childOrgIds);
        List<Contract> contracts = contractRepository.findAll();
        LocalDate now = LocalDate.now();
        // 如果不是管理员，则不能看到
        if (!isAdmin) {
            clients.clear();
            carriers.clear();
        }
        for (Client client : clients) {
            Contract contract = contracts.stream().filter(con -> con.getBelongId().equals(client.getId())).findFirst().orElse(null);
            if (contract == null) continue;
            if (contract.getExpiresDate() != null) {
                if (contract.getExpiresDate().isBefore(now)) {
                    messages.add("客户【" + client.getName() + "】合同已于【" + contract.getExpiresDate().toString() + "】到期,请及时处理");
                } else if (now.plusDays(15).isAfter(contract.getExpiresDate())) {
                    String text = contract.getExpiresDate() == null ? "到期日期未填写" : contract.getExpiresDate().toString();
                    messages.add("客户【" + client.getName() + "】合同将于【" + text + "】到期,请及时处理");
                }
            } else if (contract.getRemindDate() != null && now.isAfter(contract.getRemindDate())) {
                messages.add("客户【" + client.getName() + "】合同到期提醒,请及时处理");
            }
        }
        for (Carrier carrier : carriers) {
            Contract contract = contracts.stream().filter(con -> con.getBelongId().equals(carrier.getId())).findFirst().orElse(null);
            if (contract == null) continue;
            if (contract.getExpiresDate() != null) {
                if (contract.getExpiresDate().isBefore(now)) {
                    messages.add("供应商【" + carrier.getName() + "】合同已于【" + contract.getExpiresDate().toString() + "】到期,请及时处理");
                } else if (now.plusDays(15).isAfter(contract.getExpiresDate())) {
                    String text = contract.getExpiresDate() == null ? "到期日期未填写" : contract.getExpiresDate().toString();
                    messages.add("供应商【" + carrier.getName() + "】合同将于【" + text + "】到期,请及时处理");
                }
            } else if (contract.getRemindDate() != null && now.isAfter(contract.getRemindDate())) {
                messages.add("供应商【" + carrier.getName() + "】合同到期提醒,请及时处理");
            }
        }
        return messages;
    }

    public List<DataDto> countByBusinessType() {
        List<BusinessCode> businessCodes = businessCodeRepository.findAll();
        List<DataDto> dataDto = new ArrayList<>();
        businessCodes.forEach(businessCode -> {
            DataDto dto = new DataDto();
            dto.setBusinessType(businessCode.getName());
            dto.setCount(zadOrderRepository.countByBusinessCode(businessCode.getName()));
            dto.setCount(dto.getCount() + ghOrderRepository.countByBusinessCode(businessCode.getName()));
        });
        return dataDto;
    }

    public List<DataDto> countByOriginPlace() {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll();
        List<DataDto> dataDtos = new ArrayList<>();
        Map<String, List<ZadOrder>> group = zadOrders.stream().filter(order -> StringUtils.hasText(order.getOriginPlace())).collect(Collectors.groupingBy(ZadOrder::getOriginPlace));
        group.forEach((originPlace, data) -> {
            DataDto dataDto = new DataDto();
            dataDto.setOriginPlace(originPlace);
            dataDto.setCount(data.size());
        });
        return dataDtos;
    }

    public List<DataDto> countByName() {
        List<DataDto> dataDtos = new ArrayList<>();
        List<ZadOrder> zadOrders = zadOrderRepository.findAll();
        List<GhOrder> ghOrders = ghOrderRepository.findAll();
        List<Client> clients = clientRepository.findAll();
        List<Carrier> carriers = carrierRepository.findAll();
        clients.forEach(client -> {
            DataDto dataDto = new DataDto();
            dataDto.setType(TypeStringUtils.feeInType);
            dataDto.setName(client.getName());
            zadOrders.forEach(zadOrder -> {
                if (zadOrder.getClientId().equals(client.getId())) {
                    dataDto.addToPlace(zadOrder.getOriginPlace(), zadOrder.getDestPlace());
                }
            });
            ghOrders.forEach(ghOrder -> {
                if (ghOrder.getClientId().equals(client.getId())) {
                    dataDto.addToPlace(ghOrder.getOriginPlace(), ghOrder.getDestPlace());
                }
            });
            dataDtos.add(dataDto);
        });
        carriers.forEach(carrier -> {
            DataDto dataDto = new DataDto();
            dataDto.setType(TypeStringUtils.feeOutType);
            dataDto.setName(carrier.getName());
            zadOrders.forEach(zadOrder -> {
                if (StringUtils.hasText(zadOrder.getCarrierName()) && zadOrder.getCarrierName().contains(carrier.getName())) {
                    dataDto.addToPlace(zadOrder.getOriginPlace(), zadOrder.getDestPlace());
                }
            });
            ghOrders.forEach(ghOrder -> {
                if (StringUtils.hasText(ghOrder.getCarrierName()) && ghOrder.getCarrierName().contains(carrier.getName())) {
                    dataDto.addToPlace(ghOrder.getOriginPlace(), ghOrder.getDestPlace());
                }
            });
            dataDtos.add(dataDto);
        });
        return dataDtos;
    }


    @Data
    public static class DataDto implements Serializable {
        private long count;
        private String businessType;
        private String originPlace;
        private String name;
        private String type;
        private double amount;
        // 班列总数
        private int blCount;
        private double blAmount;
        // 国行总数
        private int ghCount;
        private double ghAmount;
        private List<FeeItemDto> fees = new ArrayList<>();
        private List<PlaceCount> placeCounts = new ArrayList<>();

        private List<OrderCount> orderCounts = new ArrayList<>();


        public void addBLCount() {
            blCount += 1;
        }

        public void addGHCount() {
            ghCount += 1;
        }

        public void addBLAmount(double amount) {
            blAmount += amount;
        }

        public void addGHAmount(double amount) {
            ghAmount += amount;
        }

        public void addCount(String type, String name, long countNumber) {
            OrderCount count = orderCounts.stream().filter(orderCount -> orderCount.getType().equals(type) && orderCount.getName().equals(name)).findFirst().orElse(null);
            if (count != null) {
                count.setCount(count.getCount() + countNumber);
            } else {
                count = new OrderCount();
                count.setType(type);
                count.setName(name);
                count.setCount(countNumber);
                orderCounts.add(count);
            }
        }

        public void addToPlace(String originPlace, String destPlace) {
            if (StringUtils.isEmpty(originPlace) || StringUtils.isEmpty(destPlace)) {
                return;
            }
            PlaceCount dto = placeCounts.stream().filter(fee -> fee.getPlace().equals(originPlace + "->" + destPlace)).findFirst().orElse(null);
            if (dto != null) {
                dto.setCount(dto.getCount() + 1);
            } else {
                dto = new PlaceCount();
                dto.setPlace(originPlace + "->" + destPlace);
                dto.setCount(1);
                placeCounts.add(dto);
            }
        }

        public void addFee(String currency, double amount, String type, String name) {
            FeeItemDto dto = fees.stream().filter(fee -> fee.getName().equals(name) && fee.getCurrency().equals(currency) && fee.getType().equals(type)).findFirst().orElse(null);
            if (dto != null) {
                dto.setAmount(dto.getAmount() + amount);
            } else {
                dto = new FeeItemDto();
                dto.setCurrency(currency);
                dto.setAmount(amount);
                dto.setType(type);
                dto.setName(name);
                fees.add(dto);
            }
        }
    }

    @Data
    public static class OrderCount implements Serializable {
        private String name;
        private String type;
        private long count;
    }

    @Data
    public static class PlaceCount implements Serializable {
        private String place; // 起始站->目的站
        private long count;
    }

    @Data
    public static class FeeItemDto implements Serializable {
        private String currency;
        private double amount;
        private String type;
        private String name;
    }
}
